package me.yushuai.wukong.base.service.impl;

import me.yushuai.wukong.base.api.dto.CreateLocGroupDTO;
import me.yushuai.wukong.base.api.dto.LocGroupDTO;
import me.yushuai.wukong.base.api.dto.UpdateLocGroupDTO;
import me.yushuai.wukong.base.convertor.LocGroupConvertor;
import me.yushuai.wukong.base.entity.LocGroup;
import me.yushuai.wukong.base.repository.LocGroupRepository;
import me.yushuai.wukong.base.service.LocGroupService;
import me.yushuai.wukong.common.api.AbstractPageable;
import me.yushuai.wukong.common.dto.PageDTO;
import me.yushuai.wukong.common.error.WuKongException;
import me.yushuai.wukong.common.exceptin.NotExistException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 库位组业务实现类
 *
 * @author zhoushuai@189.cn
 * @since 2022-08-31
 */
@Service
public class LocGroupServiceImpl implements LocGroupService {

    public static final String LOC_GROUP_ID_NOT_FOUND = "locGroup.locGroupId.notFound";

    public static final String LOC_GROUP_CODE_HAS_EXIST = "locGroup.locGroupCode.HasExist";

    public static final String LOC_GROUP_ID_AND_LOC_GROUP_CODE_NOT_MATCH = "locGroup.locGroupIdAndLocGroupCode.NotMatch";

    private final LocGroupRepository locGroupRepository;

    @Autowired
    public LocGroupServiceImpl(LocGroupRepository locGroupRepository, LocGroupConvertor locGroupConvertor) {
        this.locGroupRepository = locGroupRepository;
    }

    @Override
    public Long createLocGroup(CreateLocGroupDTO createLocGroupDTO) throws WuKongException {
        //1. 断言库位编码不存在
        this.assertLocGroupCodeNotExist(createLocGroupDTO.getLocGroupCode());

        //2. 新增库位组
        final LocGroup locGroup = new LocGroup();
        BeanUtils.copyProperties(createLocGroupDTO, locGroup);
        this.locGroupRepository.save(locGroup);
        return locGroup.getGroupId();
    }

    @Override
    public Long updateLocGroup(UpdateLocGroupDTO updateLocGroupDTO) throws WuKongException {

        //1. 检查库位组是否存在
        final Long locGroupId = updateLocGroupDTO.getGroupId();
        final String locGroupCode = updateLocGroupDTO.getBizId();
        this.assertLocGroupIdAndLocGroupCodeMatch(locGroupId, locGroupCode);

        //2. 通过库位组编号更新库位组信息
        final LocGroup locGroup = new LocGroup();
        BeanUtils.copyProperties(updateLocGroupDTO, locGroup);

        //注：库位信息采用乐观锁处理并发，此处必须传入版本号
        this.locGroupRepository.save(locGroup);

        //3. 返回库位组编码
        return updateLocGroupDTO.getGroupId();
    }


    @Override
    public void deleteLocGroup(Long locGroupId, Long version) throws WuKongException {
        //1. 构建删除条件
        final Specification<LocGroup> spec = (root, query, criteriaBuilder) -> criteriaBuilder.and(
                criteriaBuilder.equal(root.get("locGroupId").as(Long.class), locGroupId),
                criteriaBuilder.equal(root.get("version").as(Long.class), version)
        );

        if (locGroupRepository.count(spec) == 0) {
            throw new NotExistException(locGroupId, LocGroup.class);
        }

        //2. 执行删除库位组
        this.locGroupRepository.deleteByLocGroupIdAndCurVerNo(locGroupId, version);

    }

    @Override
    public LocGroupDTO findOne(Long locGroupId) throws WuKongException {

        //1. 通过locGroupId查询库位组信息
        final LocGroup locGroup = this.locGroupRepository.findById(locGroupId)
                //2. 检查库位组信息是否存在并抛出异常
                .orElseThrow(() -> new NotExistException(locGroupId, LocGroup.class));

        //3. 返回查询结果
        final LocGroupDTO result = new LocGroupDTO();
        BeanUtils.copyProperties(locGroup, result);
        return result;
    }


    @Override
    public PageDTO<LocGroupDTO> pagingSearchLocGroup(AbstractPageable pageable, Specification<LocGroup> spec) {
        //1. prepare query conditions
        Pageable page = PageRequest.of(pageable.getCurrent(), pageable.getSize());

        Specification<LocGroup> querySpec = spec;
        if (querySpec == null) {
            querySpec = (root, query, criteriaBuilder) -> query.getRestriction();
        }


        //2. execute query command
        final Page<LocGroup> locGroupPage = this.locGroupRepository.findAll(querySpec, page);
        final List<LocGroupDTO> list = locGroupPage.getContent().stream()
                .map(v -> {
                    final LocGroupDTO dto = new LocGroupDTO();
                    BeanUtils.copyProperties(v, dto);
                    return dto;
                }).collect(Collectors.toList());

        //3. transition LocGroup to LocGroupDTO and return PageDTO
        pageable.setTotal(locGroupPage.getTotalElements());
        return PageDTO.of(pageable, list);
    }

    @Override
    public void assertLocGroupIdExist(Long locGroupId) throws WuKongException {
        if (!this.locGroupRepository.existsById(locGroupId)) {
            throw new NotExistException(locGroupId, LocGroup.class);
        }
    }

    @Override
    public void assertLocGroupIdAndLocGroupCodeMatch(Long locGroupId, String locGroupCode) throws WuKongException {
        //1. prepare query conditions
        final Specification<LocGroup> spec = (root, query, criteriaBuilder) -> criteriaBuilder.and(
                criteriaBuilder.equal(root.get("locGroupId").as(Long.class), locGroupId),
                criteriaBuilder.equal(root.get("locGroupCode").as(String.class), locGroupCode)
        );

        if (this.locGroupRepository.count(spec) == 0) {
            throw new WuKongException(LOC_GROUP_ID_AND_LOC_GROUP_CODE_NOT_MATCH, new Object[]{locGroupId, locGroupCode});
        }
    }

    @Override
    public void assertLocGroupCodeNotExist(String bizId) throws WuKongException {
        final Specification<LocGroup> spec = (root, query, criteriaBuilder) -> criteriaBuilder.and(
                criteriaBuilder.equal(root.get(LocGroup.BIZ_ID).as(String.class), bizId)
        );

        if (this.locGroupRepository.count(spec) > 0) {
            throw new WuKongException(LOC_GROUP_CODE_HAS_EXIST, bizId);
        }
    }

}